home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / The World of Computer Software.iso / muzsrc1.zip / MUZIKA.ASC < prev    next >
Text File  |  1980-01-01  |  56KB  |  930 lines

  1. Technion - Israel Institute of Technology
  2. Faculty of Electrical Engineering
  3. Laboratory of Computer Music
  4.  
  5. MUZIKA - A Musical Notes and Scores Editor
  6. Internal Description of the Software
  7.  
  8. Presented By: Lavy Libman & Yakov Aglamaz
  9. Conducted By: Noam Amir
  10.  
  11. Contents:
  12. 1. Introduction
  13. 2. General description of the database
  14. 3. General description of the program modules
  15.  
  16.  
  17. 1. Introduction
  18.  
  19. This part of the MUZIKA documentation intends to describe the internal 
  20. structure of the software. We shall assume that the reader has learnt the 
  21. User's Guide and knows how the editor looks and "feels", and how to operate 
  22. the different features in it. We also assume the reader to be an experienced 
  23. programmer in the C++ language under the Microsoft Windows environment 
  24. (preferably using Borland C++ for Windows, as this is the compiler which was 
  25. used to compile the software). No general C++ or Windows terms, unless 
  26. related specifically to MUZIKA, will be explained herein.
  27.  
  28. While reading the internal documentation, it should be remembered that the 
  29. MUZIKA project is not a finished, commercial-style application. Indeed, from 
  30. the very start the intention of the project was to write the minimum amount 
  31. of code to implement the features required from a musical score editor, 
  32. stressing the ease with which the code can be extended and reused.
  33.  
  34. The program consists altogether of about 5400 lines of C++ code (not 
  35. counting the source code of the class libraries provided by Borland), making 
  36. an executable file of about 120K. We hope that this description of the 
  37. internal structure of the code, as well as the extensive commenting in the 
  38. code itself, will help the reader clarify how MUZIKA functions from the 
  39. inside.
  40.  
  41.  
  42. 2. General description of the database
  43.  
  44. Before we explain how the software functions from the inside, it is 
  45. necessary to explain the structure of the database which keeps the melody in 
  46. memory. In light of the database description, it will be easier to explain 
  47. the tasks performed by the different functions in the software through the 
  48. changes they make to the data in the database. We recommend the reader, as 
  49. he goes through the explanations below, to browse at the MUZIKA.H file, 
  50. which contains all kinds of global definitions relevant to different modules 
  51. of the software, including the definitions of the classes that comprise the 
  52. melody database.
  53.  
  54. 2.1. The melody database
  55.  
  56. At the very top, the melody has a few global parameters, followed by a list 
  57. of parts. Each part has its information kept separately from other parts; 
  58. the structure of a part will be explained later. This is implemented by 
  59. having the Melody class - the topmost class in the database, of which there 
  60. is precisely one instance at any given time - inherit its parameters from 
  61. the MelodyParameters class, and add to that an IndexedList of parts. The 
  62. global parameters of the melody include the staff width (in pixels); this is 
  63. global to the entire melody, for this width has to be common to all staves 
  64. in the melody if score displays are to be shown and printed. Other 
  65. parameters that may be added here can be, for example, the name of the 
  66. melody or the text color. On the other hand, the key signature and the time 
  67. signature should not be kept as melody parameters, as these can change 
  68. throughout the different sections of the music.
  69.  
  70. The IndexedList class is an extension of the Array class from Borland's 
  71. class library, designed specifically for the purpose of holding lists at the 
  72. different levels of the MUZIKA database. It is essentially an Array, with 
  73. the following functions added:
  74. - insertAt(Object &obj, int index) inserts an object at the given index in 
  75. the list, pushing the other objects from this index one forward.
  76. - detachAt(int index) removes an object from the given index in the list, 
  77. pulling objects from subsequent indexes to close the gap. The object itself 
  78. is not destroyed.
  79. - destroyAt(int index) is similar to detachAt, except that the object is 
  80. destroyed (i.e. its destructor is called).
  81. - printOn(ostream &out) writes the objects in the list to the stream by 
  82. calling the printOn virtual function once for every object in the list.
  83.  
  84. Continuing with the description of the MUZIKA database, the same idea of a 
  85. few global parameters followed by a list is now repeated at the part level: 
  86. the Part class inherits from the PartParameters class a few parameters 
  87. global to the part, and follows with an IndexedList of staves. The 
  88. parameters of a part in the current implementation are:
  89. - name is the part name.
  90. - multiplicity is the staff multiplicity, i.e. the number of single staves 
  91. that are grouped together to create a multiple staff with common bars.
  92. - editYMin is the Y-coordinate of the topmost visible line of the part. This 
  93. is  not a constant parameter of the part, but rather used for displaying to 
  94. remember which staves are visible at the moment. Keeping this parameter for 
  95. all parts, instead of just for the currently visible one, ensures that when 
  96. the display is flipped from one part to another (via the Layout/Page... menu 
  97. selection), the staves initially visible in the new part will be those that 
  98. were visible the last time the part was shown.
  99. Other parameters which a part may have can include, for example, the musical 
  100. instrument that plays the part.
  101.  
  102. Next, we come to how a staff is implemented. No surprises here; as the 
  103. reader should have guessed by now, the staff is again no more than a few 
  104. parameters followed by two lists this time: the list of point objects in the 
  105. staff and the list of the continuous objects. These lists were separated to 
  106. ease the treatment of the two object type groups, which is subtly different 
  107. in most cases. The Staff class inherits its parameters from a 
  108. StaffParameters class and has two IndexedList items corresponding to the two 
  109. object lists. The staff parameters include, in the current implementation, 
  110. only its coordinates: X, Y, and width. The Y coordinate of a staff is 
  111. invariant to display scrolling; the actual Y coordinate of a staff on the 
  112. screen is calculated as its Y coordinate minus the part's editYMin.
  113.  
  114. The PointObject and ContinuousObject classes, both derived from 
  115. MusicalObject, are the base classes defining the properties of a generic 
  116. point 
  117. object or continuous object, respectively. Every class defining a specific 
  118. musical object type (such as Note, Pause, etc.) is derived from one of these 
  119. two base classes. Actually, the MusicalObject class already defines the 
  120. properties that every musical object must have, letting the PointObject and 
  121. ContinuousObject classes only add the properties specific to the respective 
  122. object type groups.
  123.  
  124. An attribute that every musical object must have is the location of the 
  125. object relative to the staff in which it is. The location attribute can take 
  126. one of the following values (all defined in MUZIKA.H):
  127. - INSTAFF means that the object can be anywhere in the staff. Therefore, at 
  128. the time of creation, the constructor of the object receives both the X and 
  129. Y coordinates of the mouse cursor relative to the staff.
  130. - ABOVESTAFF means that the object is always located above the staff. 
  131. Therefore, at the time of creation, the constructor of the object receives 
  132. only the X coordinate of the mouse cursor.
  133. - BELOWSTAFF means that the object is always located below the staff. 
  134. Objects having this attribute value are treated similarly to those which are 
  135. ABOVESTAFF.
  136. - ABOVEMULTIPLE means that the object is always located above the multiple 
  137. staff. Therefore, at the time of creation, no matter where the mouse was 
  138. clicked, the object is inserted in the appropriate list of the topmost staff 
  139. of the multiple staff.
  140. - BELOWMULTIPLE means that the object is always located below the multiple 
  141. staff. Therefore, at the time of creation, no matter where the mouse was 
  142. clicked, the object is inserted in the appropriate list of the bottommost 
  143. staff of the multiple staff.
  144. - COMMONMULTIPLE means that a copy of the object will always be in every 
  145. staff in the multiple staff. Therefore, at the time of creation, no matter 
  146. where the mouse was clicked, a copy of the object is inserted in the list of 
  147. every staff comprising the multiple staff.
  148. In addition, the value of ONEPERSTAFF can be added to any of the values 
  149. above to indicate that only one such object can exist in a staff. Thus, for 
  150. example, a Key object has a location attribute of INSTAFF+ONEPERSTAFF, 
  151. meaning that there can be only one key in a staff, and the thick bar limits 
  152. at the start or end of a staff have a location attribute of 
  153. COMMONMULTIPLE+ONEPERSTAFF, meaning that there can only be one such bar 
  154. limit per staff, and in that case there is a copy of it in every staff 
  155. comprising the multiple staff.
  156.  
  157. The MusicalObject class also defines a few member functions that every 
  158. musical object should have. These are:
  159. - Draw(HDC hDC) draws the graphic image of the object in the given display 
  160. context. This function is used to display the object.
  161. - printOn(ostream &out) saves the object data in the given output stream. 
  162. This function is used when the melody is saved in a file.
  163. - clipOn(void far &*clipboard) saves the object data in the given memory 
  164. location. This function is used during clipping (i.e. cutting or copying).
  165.  
  166. The PointObject class, derived from MusicalObject, adds the properties that 
  167. every point object must have. These include the X coordinate of the object, 
  168. and the following functions:
  169. - Format(int &X) sets the object's X coordinate to the given value. This 
  170. function is used when the part is reformatted. If a point object has any 
  171. special actions required when its coordinate is changed, they can be added 
  172. by having the specific class define its own version of this virtual function.
  173. - Width() returns the object width in pixels. Usually, this is the general 
  174. object width defined in the Layout/Page... dialog box and kept in the 
  175. pixelsPerObject variable. This function, too, is used in the process of part 
  176. reformatting.
  177. - MIDIPlay(ostream &out) writes the MIDI event which the object represents 
  178. to a temporary file. This function is used during the first pass of the MIDI 
  179. file creation.
  180. - Duration() returns the duration of the MIDI event which the object 
  181. represents, in units of 148-th of a full note. This function, too, is used 
  182. in the process of creating the temporary file during the MIDI first pass.
  183.  
  184. The ContinuousObject class, derived from MusicalObject, adds the properties 
  185. that every continuous object must have. These include the Xleft and Xright 
  186. coordinates of the object, and the following functions:
  187. - FormatLeft(int &Xleft) sets the object's Xleft coordinate to the given 
  188. value. This function is used when the part is reformatted. If a continuous 
  189. object has any special actions required when its left coordinate is changed, 
  190. they can be added by having the specific class define its own version of 
  191. this virtual function.
  192. - FormatRight(int &Xright) is similar to FormatLeft, but alters the Xright 
  193. coordinate of the object.
  194.  
  195. Summarizing what has been said about the database so far, we see that it has 
  196. a structure of a 4-level tree, at the root of which sits the melody, on the 
  197. second level - the parts, on the third level - the staves, and finally, on 
  198. the leaves - the musical objects themselves. The object-oriented design of 
  199. the database is readily seen. It should be emphasized that none of the 
  200. functions, anywhere in the software, does not - in fact, cannot - make any 
  201. assumptions about the nature of the objects at the leaves, e.g. test the 
  202. type of a specific object and act differently if it is a loudness sign than 
  203. if it is a note sign. Whatever actions are specific to the musical object 
  204. classes are made in virtual member functions of the classes themselves.
  205.  
  206. Having said this much, let us turn and see what specific musical object 
  207. types have been defined in the current implementation of MUZIKA, remembering 
  208. that this is only a minimal set, intended to demonstrate the tremendous 
  209. possibilities of the design. No doubt does this set have to be extended to 
  210. include many more class types for the application to become a really useful 
  211. tool for editing high-featured scores. Anyway, the classes that have been 
  212. implemented are:
  213. - Note is the class of note objects. The different note durations (full, 
  214. half, etc.) are distinguished by the value of the class's duration 
  215. attribute. In addition, the class defines an Y attribute representing the 
  216. note height, and eventually its frequency.
  217. - Pause is the class of pause objects. The different pause durations are 
  218. distinguished by the value of the class's duration attribute.
  219. - Key is the class of key objects. The different key types are distinguished 
  220. by the value of the class's type attribute. An instance of this class always 
  221. has a location value of INSTAFF+ONEPERSTAFF.
  222. - Beat is the class of time signatures (i.e. beats per bar). The different 
  223. time signatures are distinguished by the value of the class's type attribute.
  224. - Bar is the class of bar limits. The different bar limit types (single, 
  225. double, etc.) are distinguished by the value of the class's type attribute. 
  226. An instance of this class always has a location value of COMMONMULTIPLE, 
  227. with or without the addition of ONEPERSTAFF.
  228. - Loudness is the class of loudness point objects (forte, fortissimo, piano 
  229. and pianissimo). The different loudness signs are distinguished by the 
  230. class's loudness attribute.
  231. - Crescendo is the class incorporating the crescendo and diminuendo signs. 
  232. The two signs are distinguished by the class's direction attribute.
  233. - Text is the class of text signs and instructions above or below the 
  234. staves. The text itself (up to 16 characters) is kept in text.
  235.  
  236. This concludes the description of the part of the database classes which is 
  237. defined in the MUZIKA.H and OBJECTS.H header file. To get the impression of 
  238. how things operate at the lowest levels of the database, the reader is 
  239. invited to catch a glimpse of the member function definitions in 
  240. OBJCLASS.CPP.
  241.  
  242. 2.2. The symbols database
  243.  
  244. The second large part of the database is the database of the symbols. After 
  245. all, the user does not have a direct access to the melody database; rather, 
  246. he creates the musical objects using symbols on the left of the screen.
  247.  
  248. The symbols database is conceptually simpler than the melody database. Every 
  249. symbol has its own class, from which there is exactly one instance. All the 
  250. symbol classes are derived from the generic SymbolClass base class, which 
  251. defines the properties that every symbol must have. The symbol class 
  252. instances are held in a constant array called symbolArray.
  253.  
  254. The following properties are defined in SymbolClass:
  255. - symbolID is a unique number that identifies the symbol. The ID is not just 
  256. a random unique number; rather, it represents the symbol set where it 
  257. belongs and its number within the set. In more detail, the formula to which 
  258. a symbolID value must conform is:
  259. symbolID = 16 * (symbol set) + (symbol offset)
  260. where symbol set is the symbol set number (e.g. 1 for Notes, 2 for Keys, 
  261. etc.), and symbol offset is the offset of the symbol, counted from 0 upwards 
  262. (e.g. 0 for the topmost leftmost symbol, 1 for the topmost rightmost, etc.).
  263. - symbolType is a slightly misleading name for the attribute: it is actually 
  264. the type of the object (either POINTOBJECT or CONTINUOUSOBJECT) that is 
  265. created by the symbol.
  266. - BitmapName(LPSTR, int) returns the name of the symbol bitmap in the 
  267. resource file, without the "B_" prefix. As can be easily verified by 
  268. browsing in the resource file (MUZIKA.RES) using any resource editor, all 
  269. the bitmaps are named "B_xxxx".
  270. - DrawSymbol(HDC, HDC, BOOL) draws the symbol in the given display context 
  271. at the coordinates that are calculated from its own symbolID. This function 
  272. uses the previous one to load the symbol bitmap from the resource file. The 
  273. bitmap is drawn in reverse video if the symbol is active.
  274. - CreateObject(int, int, int) creates the object corresponding to the 
  275. symbol. Sometimes, an single object type (such as Note) is created by 
  276. several symbols; in that case, it is the duty of CreateObject to construct 
  277. the appropriate version of the object and load its attribute variables with 
  278. the proper values.
  279.  
  280. It is pointless to give here a list of all the symbol classes derived from 
  281. SymbolClass. Far better is to direct the reader to the SYMCLASS.CPP file, 
  282. which is actually a library of the derived symbol classes and their versions 
  283. of the three virtual functions defined in SymbolClass, above. As with the 
  284. library of musical classes, we would like to emphasize how straightforward 
  285. it is to add a new symbol to the library: just define the new class, derived 
  286. from SymbolClass, and write its own versions for the three virtual functions 
  287. BitmapName, DrawSymbol, and most important, CreateObject, all after having 
  288. designed the symbol bitmap in the resource file (MUZIKA.RES) - and that is 
  289. all there is to it.
  290.  
  291.  
  292. 3. General description of the program modules
  293.  
  294. The source code of MUZIKA consists of over 5400 lines of C++ lines, not 
  295. including the sources of Borland's class library. Naturally, a project of 
  296. this size could not have been maintained without some sort of dividing the 
  297. code into smaller modules. In fact, MUZIKA contains 16 *.CPP files, 
  298. corresponding to logical modules of the software, containing functions that 
  299. take care of different tasks. In addition to that, we should mention the *.H 
  300. header files, especially MUZIKA.H and OBJECTS.H, that although do not 
  301. contain any actual statements, they provide the definitions of classes, 
  302. global variables, and function prototypes to be used by all the modules.
  303.  
  304. In the sequel, we attempt to describe the operation of each module 
  305. independently from the others, as much as this proves possible. When it was 
  306. not possible, we ordered the module descriptions in such a way that there 
  307. would only be references to modules described previously.
  308.  
  309. In our descriptions, we do not provide an exact list of the functions, their 
  310. parameters and return values. We feel that that would be a rather useless 
  311. duplication of the source code itself, which is well and extensively 
  312. documented as it is. Instead, we concentrate on the duty that the module 
  313. fulfills within the overall design, and explain the implemented algorithms 
  314. on the conceptual level. Either way, we recommend the reader to browse 
  315. through the enclosed source code listings as he reads the description; this 
  316. will without doubt help in understanding the logic behind the software 
  317. design.
  318.  
  319. 3.1. The main module (MAIN.CPP)
  320.  
  321. The main module contains several initialization functions and the main 
  322. window message-processing and painting functions. The entry point to the 
  323. program is at the beginning of this module, in function WinMain, which does 
  324. no more than calling the initialization functions and establishing a 
  325. message-dispatching loop, as is customary in every Windows application. The 
  326. initialization functions register the main window class, create and display 
  327. the main and edit windows for the first time. The initialization code is 
  328. entirely straightforward and is not much different from what can be found in 
  329. any other Windows application.
  330.  
  331. The main window function processes messages originated by Windows that are 
  332. intended for the main window. For example, a WM_COMMAND message, indicating 
  333. that the user has triggered an action using the menu, causes it to call the 
  334. ProcessMenu function in the menu processing module. A WM_LBUTTONDOWN 
  335. message, sent when the left mouse button is clicked, causes a call to the 
  336. symbol identification function in the symbols module, to check if the cursor 
  337. has been clicked on a symbol and make it active if so. Finally, a WM_PAINT 
  338. message, sent when the main window needs repainting (i.e. when it has been 
  339. resized, or another window has been removed from over it), triggers a call 
  340. to the PaintEditWindow function, described next.
  341.  
  342. The PaintEditWindow function is in charge of painting the edit window. It 
  343. draws the lines separating between the symbols, and then calls the 
  344. symbol-drawing functions in the symbols module.
  345.  
  346. To summarize, then, the main module play a role of a dispatcher; any 
  347. messages intended for the main window are, this is true, recognized here, 
  348. but the actions taken in response are actually a part of other modules 
  349. (specifically the menu processing module and the symbols module).
  350.  
  351. 3.2. The IndexedList class module (INDEXED.CPP)
  352.  
  353. The IndexedList module is not as important by itself as it is for other 
  354. modules, that use the functions defined here constantly. In a previous 
  355. chapter we have already explained the use of the IndexedList class, which is 
  356. an extension of the Array class from Borland's class library, in the melody 
  357. database. The functions that actually extend IndexedList over Array are 
  358. defined here. These are the insertAt, detachAt, destroyAt, and printOn 
  359. functions, all except the last similar to those of Array but keep the list 
  360. elements at contiguous indexes at all times (as opposed to an Array, which 
  361. allows for empty slots in the list).
  362.  
  363. 3.3. The database module (DATABASE.CPP)
  364.  
  365. The name of the database module may be slightly misleading: functions that 
  366. make changes in the database are scattered throughout all parts of the 
  367. software, without any central database management unit that would take care 
  368. of keeping the database intact. What there actually is in the database 
  369. module are member functions of classes that comprise the database. Among the 
  370. functions defined here for all the class levels (Melody, Part, and Staff) 
  371. are LoadFrom, which loads the appropriate class instance data from a file, 
  372. and printOn, which does just the opposite - saves the class instance data in 
  373. a file. There are also a few class constructor and destructor functions. 
  374. Finally, the only Melody class instance is declared at the file end. The 
  375. code in this module is straightforward and contains no subtleties; we feel 
  376. therefore confident that the commenting of the code in the source file 
  377. itself provides a sufficient explanation.
  378.  
  379. 3.4. The display module (DISPLAY.CPP)
  380.  
  381. The display module is responsible for displaying the information stored in 
  382. the melody database in either format supported by MUZIKA (single part or 
  383. score, in the current implementation). There are two important functions in 
  384. the module, namely EditWindowProc, the edit window function, and 
  385. PaintEditWindow, which is responsible for displaying staves and objects in 
  386. the edit window. The other two functions, used only during initialization, 
  387. are less important. By the way, the edit window is a child of the main 
  388. window, containing the main window client area minus the area that is 
  389. occupied by the symbols and status line. The edit window is automatically 
  390. resized every time the main window changes its size. Staves and musical 
  391. signs are drawn only in the edit window.
  392.  
  393. The EditWindowProc function responds to all messages intended for the edit 
  394. window, including cursor movement, mouse button clicks and double-clicks, 
  395. and scrollbar changes. Covering this quite complex function's tasks is 
  396. impossible without elaborating on the way it responds to each and every 
  397. message type, so let's do just that.
  398.  
  399. A WM_PAINT message, requesting a repaint of the edit window, is treated by 
  400. the PaintEditWindow function, described later in this section. A 
  401. WM_MOUSEMOVE message does not offer any cause for alarm, either - all that 
  402. has to be done is change the cursor shape according to the current edit mode 
  403. symbol (pencil, eraser or hand).
  404.  
  405. The problems begin with the WM_LBUTTONDOWN message, because a simple 
  406. clicking of the left button can trigger a variety of actions, according to 
  407. what symbol is active at that moment. In any case, the actions which 
  408. eventually lead to making the required changes in the database are done in 
  409. the edit module (EDIT.CPP), but it is the duty of EditWindowProc to select 
  410. the operation to be triggered. First of all, EditWindowProc checks that a 
  411. single part is displayed (no editing is allowed on a score display). Then, 
  412. it checks what the current symbol is.
  413. - If it is the pencil, a staff creating function in the edit module is 
  414. called.
  415. - If it is the eraser, an object deleting function in the edit module is 
  416. called.
  417. - If it is a symbol whose symbolType is POINTOBJECT, a point object 
  418. inserting function in the edit module is called.
  419. In the remaining cases (the active symbol is the hand or corresponds to a 
  420. CONTINUOUSOBJECT), no operation can be triggered just yet, because we need 
  421. the point where the mouse button is released as well as where it is clicked. 
  422. In that case, the mouse is captured to the edit window and the capture mode 
  423. variable is set accordingly, to let the required operation to be completed 
  424. after the mouse button is released.
  425.  
  426. A WM_LBUTTONDBLCLK is processed similarly: if the current symbol is the 
  427. eraser, the staff erasing function is called immediately, while if the 
  428. current symbol is the hand, the mouse is captured until the time it is 
  429. released, and only then will the staff movement operation be completed.
  430.  
  431. In light of the above, a mouse button release notification message cannot be 
  432. just thrown away: an operation may need to be completed at this stage. 
  433. Therefore, in response to a WM_LBUTTONUP message, EditWindowProc checks 
  434. whether the mouse has been captured to the edit window, indicating that 
  435. there is indeed an unfinished operation pending for the mouse button release 
  436. point. The nature of the unfinished operation is kept in the capture 
  437. variable. Whatever the operation is (inserting a new continuous object or 
  438. moving an object or a staff), the appropriate function in the edit module is 
  439. called with the mouse button clicking and releasing points.
  440.  
  441. There is yet another message which EditWindowProc has to process, and that 
  442. is WM_VSCROLL, indicating an operation of some kind on the vertical scroll 
  443. bar (line up, line down, page up, page down, or direct thumb movement). In 
  444. response to the message, the thumb position is set to its new location, and 
  445. the screen is refreshed. By the way, the scroll bar range is identical at 
  446. all times to the range of Y coordinates of the staves in the current part 
  447. (that is, the scroll bar values range from 0 to the Y coordinate of the last 
  448. staff).
  449.  
  450. To summarize, EditWindowProc takes much of the sting out of the editing 
  451. process: it makes the relatively difficult decision of which operation is to 
  452. be performed, leaving the edit module functions only to update the database 
  453. given all the needed coordinates.
  454.  
  455. The other nontrivial function in the display module, as we have noted, is 
  456. PaintEditWindow, whose job is to display a part of the melody database on 
  457. the screen. The display algorithm is similar whether it is a single-part 
  458. display or a score display. In both cases, the relevant staves are drawn 
  459. (using the Staff class Draw function, also included in this module at the 
  460. end of the file), and the musical signs are drawn on those staves that 
  461. reported that they were not entirely clipped, by going through the point and 
  462. continuous object lists, calling the Draw virtual function for every item in 
  463. the lists. The difference is only which staves are drawn; obviously, when 
  464. displaying a score the parallel staves of all parts take part in the 
  465. algorithm, while when displaying a single part only that part's staves are 
  466. included. Anyway, after having drawn the staves, the status line in the main 
  467. window is updated with information about which staves are visible. Strictly 
  468. speaking, having the main window updated in one of its children's window 
  469. function is a violation of the Windows "correct" programming style; what 
  470. should have been done instead is define the status line area as another 
  471. child window and have the edit window send an update message to that child 
  472. whenever it needed an update, but the implemented solution was chosen for 
  473. the sake of saving space (and even more modules).
  474.  
  475. 3.5. The edit module (EDIT.CPP)
  476.  
  477. The edit module functions are responsible for updating the melody database 
  478. in response to a edit operation initiated by the user. Except for the first 
  479. couple, all the functions are called from the display module, after the edit 
  480. window function has selected the operation and obtained the cursor screen 
  481. coordinates for it. The operations supported here include inserting, 
  482. deleting and moving staves and point and continuous objects. To understand 
  483. how these operations are taken care of the reader should have a clear view 
  484. of the melody database structure, for which he should refer to section 2.1.
  485.  
  486. Basically, all the edit functions operate similarly: they scan an 
  487. appropriate list (either a list of staves in a part or of objects in a 
  488. staff) in an attempt to find the list index where the operation takes place, 
  489. and then insert or delete at that index, according to the operation that 
  490. needs to be done. There are, however, certain subtleties here and there, 
  491. that need specific care to be taken (a value of COMMONMULTIPLE for an 
  492. object's location attribute, for example). The code of the functions is well 
  493. commented, but we still feel that a general explanation of each function's 
  494. task here will be helpful.
  495.  
  496. InsertEmptyStaff inserts a new empty staff in the specified staves list 
  497. given the staff Y location and the part multiplicity. The list is scanned 
  498. for a staff having a Y coordinate larger that the inserted staff's one, and 
  499. the new staff is inserted at that index. All the subsequent staves get their 
  500. Y coordinate moved down by a staff height. Therefore, the list is kept 
  501. sorted by the Y locations in ascending order. Also, if there is a marked 
  502. block past where the staff is inserted, the block marking variables are 
  503. incremented too.
  504.  
  505. NewMultipleStaff creates a new multiple staff, after the user has clicked 
  506. the pencil-on-staff symbol. A number of single staves equal to the part's 
  507. staff multiplicity is inserted in the part's staff list by calling 
  508. InsertEmptyStaff that number of times. The scroll bar range is readjusted to 
  509. the new Y coordinate range (recall that the scroll bar range, from Windows' 
  510. point of view, is from 0 to the last staff's Y coordinate).
  511.  
  512. IdentifyStaff finds the index of the staff to which the given Y coordinate 
  513. (obtained from the current cursor position) is closest. It does this by 
  514. scanning the list of staves, returning one which actually contains the 
  515. point, if it exists. Failing that, it returns the staff from which the 
  516. point's Y distance is minimal, but in any case not more than half a staff's 
  517. total width, as defined in the page layout dialog box and kept in the 
  518. pixelsPerStaff global variable.
  519.  
  520. DeleteMultipleStaff deletes the multiple staff identified by a Y coordinate 
  521. (obtained from the current cursor position). Having identified the staff 
  522. using IdentifyStaff, the function simply removes all the staves in the same 
  523. multiple staff from the list. If the multiple staff contains any objects, 
  524. the user is requested to confirm the operation.
  525.  
  526. NewPointObject is probably one of the most important functions of the 
  527. software, though it is not conceptually difficult. First of all, it creates 
  528. a point object corresponding to the active symbol at the given cursor 
  529. position (using the current symbol's CreateObject function - there is no 
  530. idea about what the object is). Next, it checks the just created object's 
  531. location attribute to decide where to insert it. If the object has a 
  532. location value of INSTAFF, ABOVESTAFF, or BELOWSTAFF, it is simply inserted 
  533. in the staff identified by IdentifyStaff. An object with a location value of 
  534. ABOVEMULTIPLE or BELOWMULTIPLE is inserted in the top or bottom staff of the 
  535. multiple staff, respectively. Finally, in case of a COMMONMULTIPLE object, a 
  536. copy of it is inserted in every staff comprising the multiple staff. In 
  537. addition to all of the above, if the object has the ONEPERSTAFF bit set, the 
  538. staff is checked not to contain another object at the same place, reporting 
  539. an error if this condition is not met.
  540.  
  541. NewContinuousObject is mostly similar to NewPointObject, just described. It 
  542. creates a continuous object corresponding to the active symbol at the given 
  543. cursor coordinate), and inserts it in one of the staves from which the 
  544. multiple staff is constructed, according to the object location attribute.
  545.  
  546. DeleteMusicalObject may look frightening at first sight, considering the 
  547. amount of code in it, but this actually follows from the majority of special 
  548. cases that need to be taken care of. Basically, it scans the point and 
  549. continuous object lists of the staff identified by IdentifyStaff, deleting 
  550. the objects that are within (pixelsPerObject/2) pixels away from the given 
  551. cursor position. If any of the deleted objects is COMMONMULTIPLE, the other 
  552. staves in the same multiple staff are also scanned and this object is 
  553. deleted from them too.
  554.  
  555. MoveStaff moves a multiple staff from one place to another in the same part. 
  556. Before actually moving, the destination is checked to be free from other 
  557. staves. In the process of moving (achieved by detaching each staff of the 
  558. multiple staff from the staff list and reinserting it at its new index), any 
  559. staves that the moving has crossed (and are now on a different side relative 
  560. to the moved staff) get their Y coordinate updated, and so is done to the 
  561. block marking variables in case there was a marked block in the area.
  562.  
  563. MoveMusicalObject moves a musical object by using the CutBlock and 
  564. PasteBlock functions in BLOCK.CPP. Therefore, to understand the object 
  565. moving operation, the user is referred to the description of the block 
  566. operations module later in this chapter.
  567.  
  568. 3.6. The menu processing module (MENU.CPP)
  569.  
  570. The job of this module is to dispatch various requests made by the user 
  571. through the main menu to the modules that take care of them. The ProcessMenu 
  572. function is thus not more than one big switch statement, interpreting the 
  573. menu item codes and either calling the appropriate functions in other 
  574. modules directly or by creating a dialog box function instance first.
  575.  
  576. Another function in this module is InitializeMenu, used during the software 
  577. initialization. According to the Boolean parameter it receives, it enables 
  578. or grays out the menu items that cannot be used before a melody is loaded in.
  579. 3.7. The file operations module (FILE.CPP)
  580.  
  581. The file operations module is an interface to the functions offered to the 
  582. user by the File menu, namely creating a new melody, loading and saving a 
  583. melody, and converting a melody to a MIDI file. The functions themselves are 
  584. not implemented in this module; what is rather contains is the functions of 
  585. the dialog boxes created when the user selects different file operations.
  586.  
  587. The first function in the source file is AskSave, which does the job of 
  588. confirming any operation that may destroy the current melody (such as 
  589. loading a new one or quitting the application) when the current melody has 
  590. been modified since the last time is was saved. The information about 
  591. whether the melody has been modified is kept in the melodyModified global 
  592. variable, which is set to TRUE by all the editing functions that modify the 
  593. melody, and to FALSE by the saving function.
  594.  
  595. The rest of the module functions are straightforward dialog box functions, 
  596. whose duty is to process typical dialog box messages, such as WM_INITDIALOG 
  597. to initialize a dialog box or WM_COMMAND to respond to an action made by the 
  598. user. Actually, they differ little except for what they do after the user 
  599. selects OK to confirm his selections:
  600. - DialogNew empties the current melody from any parts it might have 
  601. contained, and creates a new melody with a single part named UNNAMED, with a 
  602. staff multiplicity of 1. It also puts default values into a few global 
  603. variables, such as melodyModified and scoreDisplay.
  604. - DialogOpen opens the file whose name the user has given (reporting an 
  605. error if that file does not exist) and issues a call to the LoadFrom 
  606. function of the Melody class to load the melody.
  607. - DialogSaveAs opens the file whose name the user has given (issuing a 
  608. warning if that file already exists) and calls the printOn function of the 
  609. Melody class to save the melody.
  610. - DialogCreateMIDI calls the MIDI file creation module to convert the 
  611. current melody to a MIDI file.
  612.  
  613. 3.8. The printing module (PRINT.CPP)
  614.  
  615. The printing module contains the functions that make a hard copy of a melody 
  616. or of one of its parts on a printer. In any case, the printer selected as 
  617. the Windows default is used. There are two functions, PrintSinglePart and 
  618. PrintScore, that take care of the process of printing a single part or the 
  619. score of all parts. The interface function to the module is PrintEditWindow, 
  620. which creates a printer device context and calls one of the other two 
  621. printing functions according to what the current display setting.
  622.  
  623. Both printing functions operate quite similarly, the main difference being 
  624. the source of their staves, taken either from only one part or all parts, 
  625. respectively. Both use the staves' and objects' Draw functions to create a 
  626. multiple staff image in the printer device context and then flush it to the 
  627. printer. The multiple staves are therefore printed one by one.
  628.  
  629. 3.9. The layout module (LAYOUT.CPP)
  630.  
  631. The layout module implements the layout dialog box functions. Specifically, 
  632. it contains three functions: 
  633. - DialogParts, the Layout/Parts... dialog box function;
  634. - DialogPage, the Layout/Page... dialog box function;
  635. - DialogNewPart, the Layout/Parts.../New... dialog box function.
  636.  
  637. The dialog box functions code is tailored to the specific form of the dialog 
  638. boxes themselves, designed in the MUZIKA.RES resource file under the names 
  639. D_PARTS, D_PAGE and D_NEWPART respectively. To understand the code, the 
  640. reader must try using the MUZIKA editor for a while and get the feel of how 
  641. the mentioned dialog boxes look, but once that stage is over, the code is as 
  642. straightforward as any dialog box function code usually is. All the three 
  643. functions respond to messages intended for their dialog boxes, such as 
  644. WM_INITDIALOG to initialize a dialog box (which means, for the mentioned 
  645. dialog boxes, to fill the list boxes with the lists of part names), and 
  646. WM_COMMAND to respond to an action made by the user. After the user selects 
  647. OK to confirm his selections, these functions complete their duties by 
  648. copying these selections to global variables, such as pixelsPerStaff for the 
  649. staff height and pixelsPerObject for the object width, both selected in the 
  650. Page dialog box. It is pointless to elaborate on any of these functions 
  651. further, as they are commented well enough for the reader to be able to 
  652. understand them easily by himself.
  653.  
  654. 3.10. The About dialog box module (ABOUT.CPP)
  655.  
  656. The About dialog box module is the smallest module of the software. As its 
  657. name suggests, it has no more than the function for the dialog box that 
  658. appears upon the user's selection of Help/About... The trivial function code 
  659. should pose no problem for the reader who has had the experience with the 
  660. File and Layout dialog box much more complicated functions.
  661.  
  662. 3.11. The reformatting module (FORMAT.CPP)
  663.  
  664. The reformatting module implements a part reformatting algorithm. In its 
  665. current version, the algorithm reformats each multiple staff by itself; bars 
  666. and musical objects do not move from one staff to another. Let the reader, 
  667. however, have no illusion; even the implemented algorithm is complicated 
  668. enough as it is, which is easily proved at least by the length of the 
  669. module. The central function of the module is FormatMultipleStaff, which 
  670. adjusts the X coordinates of musical objects on one multiple staff. The 
  671. interface function of the module is FormatEntirePart, that does no more than 
  672. calling FormatMultipleStaff once per every multiple staff contained in the 
  673. part.
  674.  
  675. Before describing the algorithm itself, a word should be said about the data 
  676. structures it uses. At the beginning of FormatMultipleStaff a few lists are 
  677. allocated on the heap for later use. The lists are as long as there are 
  678. single staves in a multiple staff, i.e. every list item holds a datum about 
  679. the single staff corresponding to it. These lists are:
  680. - duration holds the durations left (in units of 148-th of a full note) for 
  681. the current object on the staves. Whenever this duration is over, the next 
  682. object should be brought in on the corresponding staff.
  683. - index holds the indexes of the current objects for all staves.
  684. - atCommon holds TRUE for a staff who has stopped on a COMMONMULTIPLE 
  685. object. It is used to ensure that such objects remain at the same X 
  686. coordinate on all staves after the reformatting process.
  687. - staffEnd holds TRUE for a staff that has no objects left to reformat.
  688.  
  689. The next few lines create two lists combining the continuous object lists of 
  690. all the single staves, one sorted by Xleft and one sorted by Xright. This 
  691. operation is required because later, when the X coordinates are changed 
  692. considering only the point objects, the continuous objects' left and right 
  693. coordinates will not be changed simultaneously. Note that during this stage, 
  694. the continuous objects appear on three lists at once! However, this does not 
  695. contradict in any way the normal usage of the IndexedList class, holding 
  696. only pointers to the actual objects instead of copies of the objects 
  697. themselves.
  698.  
  699. Next comes the main loop of the algorithm, which lasts as long as the 
  700. multiple staff has not been completely reformatted. At every stage of the 
  701. loop the minimum value in the duration list is subtracted from all the 
  702. others. In those staves at which the duration reaches zero as a result of 
  703. the subtraction, the next object is placed at the current X position, 
  704. denoted by the currX variable. If several objects were at the same place 
  705. originally (for example, an accord), they are all moved to their new place 
  706. together. This includes the continuous objects having one of the coordinates 
  707. at the current X position as well as the point objects. To conclude the 
  708. loop, the index list of indexes is updated and the current X position is 
  709. advanced by the default object width, held in pixelsPerObject.
  710.  
  711. If the current object in any staff is a COMMONMULTIPLE object, the atCommon 
  712. flag is marked for that staff. Later, no objects will be reformatted further 
  713. in that staff before atCommon is raised to TRUE at all staves. This ensures 
  714. that COMMONMULTIPLE objects remain at a common X coordinate (though possibly 
  715. different from the original). As a consequence, objects cannot cross bar 
  716. limits; whatever is in a bar before reformatting stays there afterwards, 
  717. even if there is a logical error and the object durations do not sum up to 
  718. the same value.
  719. 3.12. The MIDI file creation module (MIDI.CPP)
  720.  
  721. The MIDI file creation functions are gathered in this module. They are 
  722. activated when the user chooses Create MIDI... from the File menu. The 
  723. length of the file witnesses that the problem of creating a MIDI file is far 
  724. from being simple. We hope to clarify our approach to the solution in the 
  725. sequel.
  726.  
  727. The conversion of a melody to a MIDI file is performed in two stages, or 
  728. passes, as they are called in the program text. During the first pass, a 
  729. temporary file is created with information about the MIDI events represented 
  730. by the objects in the melody. The second pass adds information that was not 
  731. be available during the first translation pass, such as track lengths, and 
  732. creates a final MIDI file.
  733.  
  734. The first pass is implemented by the MIDIFirstPass function, which simply 
  735. calls MIDIStaff once per every multiple staff in the score. The algorithm 
  736. implemented in MIDIStaff bears some resemblance to that of reformatting a 
  737. multiple staff (see function FormatMultipleStaff in the reformatting module 
  738. description). In particular, there are the duration and index arrays having 
  739. the same purpose.
  740.  
  741. The main loop of the first-pass translation algorithm lasts as long as the 
  742. multiple staff has not been completely reformatted. At every stage of the 
  743. loop the minimum value in the duration list is subtracted from all the 
  744. others. In those staves at which the duration reaches zero as a result of 
  745. the subtraction, the next object's MIDIPlay virtual function is called to 
  746. put a description of the MIDI event represented by that object in the 
  747. temporary file. If several objects were at the same place originally (for 
  748. example, an accord), they are all MIDIPlayed together. To conclude the loop, 
  749. the index list of indexes is updated, advancing the current object in the 
  750. staves that took part in the play.
  751.  
  752. Before we can explain the second pass of the algorithm, it is necessary to 
  753. elaborate on the format of the temporary file available after the first 
  754. pass. Ignoring for a while the meta-events defined by objects other than 
  755. notes and pauses, the temporary file contains note-on events written by the 
  756. Note objects and note-off events written by the Pause objects. The events 
  757. are simply there in the file one after another, each event keeping such 
  758. information as part number, note frequency, and most important - the 
  759. duration. However, the MIDI file format does not use durations of events but 
  760. rather delta times between events. This fact creates a translation problem 
  761. from the temporary file format to the final MIDI file format, because each 
  762. note event having any duration is translated to two MIDI events: a note-on 
  763. at the beginning and a note-off at the end of its duration. This problem is 
  764. solved in the second translation pass.
  765.  
  766. The second-pass translation could be achieved by building a list of events 
  767. from the temporary file, adding two events to the list for every note and 
  768. keeping the list ordered by the time of the note appearances. This is 
  769. precisely how MIDISecondPass works, except for two differences: one is that 
  770. there are several such lists, one of each part (kept in the event[] array), 
  771. and the other is that it does not build the list in its entirety before 
  772. writing it to the MIDI file. Instead, as soon as it becomes clear that a 
  773. specific event will not be preceded by another, that event is written to the 
  774. MIDI file. Otherwise there would have been a danger of getting out of memory 
  775. very quickly during the translation process.
  776.  
  777. The class that is instantiated in the list(s) described above is MIDIEvent. 
  778. To what every list item (of class Object) has, it adds the properties of 
  779. event code, frequency, and delta time. In the algorithm implemented in 
  780. MIDISecondPass, the lists of MIDIEvent instances are kept as delta lists, in 
  781. which the delta time of an event is its actual delta time from the previous 
  782. event on the list. Such arrangement eases the insertion and deletion of 
  783. events from the lists.
  784.  
  785. In the main loop of MIDISecondPass, events are read from the temporary file 
  786. and inserted in the part lists as described above until no lists remain 
  787. empty. At this point, it is clear that one of the events already in the 
  788. lists must come before any subsequent ones. Among the first events in the 
  789. lists, the one with the minimum delta time is chosen for writing into the 
  790. MIDI file. After it is written and removed from the list, the temporary file 
  791. is again read until no list is empty, etc. The process is finished at the 
  792. end of the temporary file, after which the events already in the lists are 
  793. flushed in an appropriate order to the MIDI file.
  794. Of course, the general structure of a MIDI file (its division to a header 
  795. and playing tracks) is preserved in the MIDISecondPass function that creates 
  796. the final-version MIDI file.
  797.  
  798. 3.13. The block operations module (BLOCK.CPP)
  799.  
  800. The block operations module defines four functions, corresponding to the 
  801. four operations that can be performed on blocks: marking, copying, cutting 
  802. and pasting. The functions are executed from the block operation symbol 
  803. class instances when the user clicks the mouse with any of these symbols 
  804. active.
  805.  
  806. The block marking function (MarkBlock) code is trivial: it only has to 
  807. assign the block marking variables, which are markBeginStaff, markBeginX, 
  808. markEndStaff and markEndX, with the proper values. These variables being 
  809. global, the display module's PaintWindowProc function can figure out where 
  810. to display the musical text in reverse video. Also, any editing operation 
  811. that may alter the block marking variables (such as inserting a new staff) 
  812. must be sure to update their values whenever a possibility of that happening 
  813. exists.
  814.  
  815. The other functions, however, are not that simple. The operation of CutBlock 
  816. and CopyBlock is extremely similar, the only difference being that CutBlock 
  817. destroys the objects it clips, whereas CopyBlock doesn't. Both allocate a 
  818. memory block in which the clipped objects' information will be written and 
  819. that will be attached to the clipboard later on. Then they loop on the 
  820. staves' point and continuous object lists, finding those objects contained 
  821. in the marked block, and call their clipOn virtual functions, that write the 
  822. objects' information in the memory block. CutBlock also removes the clipped 
  823. objects from the lists and destroys them. Finally, the memory block handle 
  824. is attached to the Windows clipboard. From now on, the information in the 
  825. clipboard is independent of the current MUZIKA application instance, and it 
  826. can be pasted in any other MUZIKA window.
  827.  
  828. PasteBlock does the reverse operation of restoring musical objects out of 
  829. the information available in a clipboard handle. During pasting, a block 
  830. that is entirely contained in one multiple staff is treated differently from 
  831. a block scattered among several multiple staves. The difference is expressed 
  832. by the fact that a one-staff block can be pasted at any location on an 
  833. existing staff, enabling objects to be moved horizontally as well, whereas 
  834. pasting a many-staves block recreates the staves with the original X 
  835. coordinates of the objects.
  836.  
  837. The pasting process is performed by reading the object types one by one from 
  838. the clipboard handle and reinstantiating them at their new places. Actually, 
  839. PasteBlock distinguishes only between a staff object and whatever is not a 
  840. staff (i.e. is a MusicalObject). Further separation to the different musical 
  841. class types is done by the PasteObject function in the musical class library 
  842. contained in OBJCLASS.CPP.
  843.  
  844. 3.14. The symbol operations module
  845.  
  846. The symbol operations module defines the functions that are common to all 
  847. symbols. This is the module that works extra hours when the user chooses a 
  848. symbol set from the Symbols submenu, or when he changes the active symbol 
  849. within a given set. The functions presented by the module are summarized 
  850. below. We recommend the reader to review the symbols database organization, 
  851. described in section 2.2.
  852.  
  853. DisplayEditModeSymbols displays the edit mode symbols on the screen top, 
  854. marking the current by reverse video. In the symbols' module terminology, 
  855. edit mode symbols are the three constantly appearing symbols at the screen 
  856. top: the pencil, the eraser and the hand. The active edit mode symbol, if 
  857. any, is marked by the variable activeEditMode. Whether an edit mode symbol 
  858. is active at all (a regular symbol from a set on the screen left can be 
  859. active instead) is marked in the variable editModeSymbolActive.
  860.  
  861. IdentifyEditModeSymbol checks if the given cursor position is on one of the 
  862. edit mode symbols. If so, it makes that symbol active and refreshes the 
  863. display accordingly.
  864.  
  865. RefreshSymbols displays the current symbol set on the screen left, marking 
  866. the current symbol, if any, by reverse video. The active symbol is specified 
  867. by the pair of variables activeSymbolSet and activeSymbol. In addition, a 
  868. pointer to the current symbol class instance is held in the currentSymbol 
  869. pointer.
  870.  
  871. IdentifySymbol checks if the given cursor position is on one of the symbols 
  872. on the screen left. If so, it makes that symbol active and refreshes the 
  873. display accordingly, updating the currentSymbol pointer.
  874.  
  875. Finally, GetActiveSymbol and GetCurrentSymbol return the current symbol, by 
  876. ID or as a pointer to the symbol class instance, respectively.
  877.  
  878. 3.15. The symbols class library (SYMCLASS.CPP)
  879.  
  880. The symbols class library gathers the definitions of the symbol classes. As 
  881. the reader will recall from section 2.2, for every symbol there is exactly 
  882. one symbol class, derived from the generic base class SymbolClass, with one 
  883. instance. In particular, a symbol class must redefine the virtual functions 
  884. defined in SymbolClass, which are BitmapName, DrawSymbol, and CreateObject. 
  885. Probably the most important of these is CreateObject, that creates a musical 
  886. object corresponding to the symbol and returns a pointer to it. Only because 
  887. it is defined as virtual for all the symbol classes does it become possible 
  888. for the editing process, especially the NewPointObject and 
  889. NewContinuousObject functions, to behave in a truly object-oriented manner, 
  890. without knowing the types of the objects on which they operate at all.
  891.  
  892. The symbol class instances - or more precisely, pointers to them - are kept 
  893. in a list called symbolList, sorted by their ID code. For information about 
  894. what a symbol ID is, refer to section 2.2. This list, declared at the end of 
  895. SYMCLASS.CPP, is searched whenever the active symbol or symbol set changes. 
  896. Except for that, there is little we can add to the above sentences. The 
  897. reader is invited to browse the SYMCLASS.CPP to see the symbol classes 
  898. define, in an entirely straightforward manner, their own versions of the 
  899. virtual functions listed above.
  900. 3.16. The musical class library (OBJCLASS.CPP)
  901.  
  902. The musical class library contains the definitions of the musical classes' 
  903. versions of the pure virtual functions defined in MusicalClass (the base 
  904. class for all musical object types). The different musical classes 
  905. themselves are defined in the OBJECTS.H header file. More specifically, each 
  906. musical class (of which there are currently seven: Note, Pause, Key, Beat, 
  907. Bar, Loudness, Crescendo, and Text) has its own versions of the following 
  908. functions:
  909. - Three different constructor functions: one that receives the instance data 
  910. directly as parameters; one that gets as a parameter the input stream from 
  911. which to read the instance data; and one that gets the instance data from a 
  912. memory block attached to the clipboard.
  913. - A Draw function, that draws the object in the given display context, using 
  914. (if needed) the display module's global variables staffX, staffY, and 
  915. staffLoc. This functions is used to draw object images during displaying and 
  916. printing.
  917. - A printOn function, that writes the object data to the given output stream 
  918. in a way that enables its complete reconstruction. This function is used 
  919. when the melody is saved.
  920. - A clipOn function, that writes the object data to the given memory block, 
  921. presumably attached to a clipboard handle. This function is used when the 
  922. object is inside a clipped (i.e. cut or copied) block.
  923.  
  924. In addition, point object classes are allowed to redefine the Format, Width, 
  925. MIDIPlay, and Duration virtual functions, and continuous object classes can 
  926. make their own versions of FormatLeft and FormatRight. This may be needed in 
  927. cases where an object has actions to perform during formatting or 
  928. translating a MIDI file other than the default ones, defined in MUZIKA.H.
  929.  
  930.